package br.com.suricattus.util.file;

import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FilenameFilter;

import br.com.suricattus.util.collections.ArrayUtil;

/**
 * Classe utilitaria para arquivos
 * 
 * @author lucas lins
 *
 */
public abstract class FileUtil {

	/**
	 * Retorna o nome do arquivo extraindo os paths
	 * @param fileName
	 * @return
	 */
	public static String getSimpleName(String fileName){
		String name = null;
		
		String[] windowsPaths = fileName.split("\\\\");
		name = windowsPaths[windowsPaths.length -1];

		String[] unixPaths = name.split("/");
		name = unixPaths[unixPaths.length - 1];
		
		return name.replace("\\", "").replace("/", "");
	}
	
	/**
	 * Retorna o nome do arquivo extraindo os paths e a extensao.
	 * @param fileName
	 * @return
	 */
	public static String getSimpleNameWithouExtension(String fileName){
		fileName = getSimpleName(fileName);
		int index = fileName.lastIndexOf('.');
		if(index < 0) return fileName;
		return fileName.substring(0, index);
	}
	
	/**
	 * Retorna o nome do arquivo extraindo a extensao.
	 * @param fileName
	 * @return
	 */
	public static String getNameWithouExtension(String fileName){
		int index = fileName.lastIndexOf('.');
		if(index < 0) return fileName;
		return fileName.substring(0, index);
	}
	
	/**
	 * Retorna a extensao do arquivo.
	 * @param fileName
	 * @return
	 */
	public static String getExtension(String fileName){
		int index = fileName.lastIndexOf('.');
		if(index < 0) return null;
		return fileName.substring(index, fileName.length()).toLowerCase();
	}
	
	/**
	 * Ajusta a extensao do arquivo para ser minuscula.	
	 * Exemplo: Teste.PDF para Teste.pdf
	 * @param fileName
	 * @return
	 */
	public static String normalize(String fileName){
		String extension = getExtension(fileName);
		return getNameWithouExtension(fileName) + (extension != null ? extension : "");
	}

	/**
	 * Similar ao comando rm -rf file
	 * @param file
	 */
	public static void rm(String file){
		rm(new File(file));
	}
	
	/**
	 * Similar ao comando rm -rf file
	 * @param file
	 */
	public static void rm(File file){
		if(file.isDirectory()) for(File child : file.listFiles()) rm(child); 
		file.delete();
	}
	
	/**
	 * Similar ao comando cp -r file to
	 * @param from
	 * @param to
	 */
	public static void cp(String from, String to){
		cp(new File(from), new File(to));
	}
	
	/**
	 * Similar ao comando cp -r file to
	 * @param from
	 * @param to
	 */
	public static void cp(File from, File to){
		if(from.isDirectory() && to.exists()){
			if(!to.isDirectory()) throw new RuntimeException(String.format("cp: %s: Not a directory",to.getAbsolutePath()));
			to = new File(to.getAbsolutePath() + File.separator + from.getName());
		}
		copy(from, to);
	}
	
	/**
	 * Similar ao comando mv file to
	 * @param from
	 * @param to
	 */
	public static void mv(String from, String to){
		mv(new File(from), new File(to));
	}
	
	/**
	 * Similar ao comando mv file to
	 * @param from
	 * @param to
	 */
	public static void mv(File from, File to){
		if(from.isDirectory() && to.exists()){
			if(!to.isDirectory()) throw new RuntimeException(String.format("mv: rename from to %s: Not a directory",to.getAbsolutePath()));
			final String fromName = from.getName();
			String[] list =  to.list(new FilenameFilter(){
										public boolean accept(File dir, String name) {
											return name.equals(fromName);
										}
									});
			if(list != null && list.length > 0) throw new RuntimeException(String.format("mv: rename from to %s: Directory not empty",to.getAbsolutePath() + File.separator + from.getName()));
			to = new File(to.getAbsolutePath() + File.separator + from.getName());
		}
		copy(from, to);
		rm(from);
	}
	
	/**
	 * Faz a copia de from para to.
	 * Caso to nao exista o comando sera similar ao cp.
	 * Caso to exista copia o conteudo de from para to
	 * @param from
	 * @param to
	 */
	public static void merge(String from, String to){
		merge(new File(from), new File(to));
	}
	
	/**
	 * Faz a copia de from para to.
	 * Caso to nao exista o comando sera similar ao cp.
	 * Caso to exista copia o conteudo de from para to
	 * @param from
	 * @param to
	 */
	public static void merge(File from, File to){
		copy(from, to);
	}
	
	/* *****************************
	 * PRIVATE METHODS
	 * *****************************/
	
	private static void copy(File from, File to){
		if(from.isDirectory()){
			if(!to.exists()) to.mkdir();
			for(File child : from.listFiles()) cp(child, new File(to.getAbsolutePath() + File.separator + child.getName()));
		}else{
			FileInputStream fis = null;
			FileOutputStream fos = null;
			try{
				fis = new FileInputStream(from);
				fos = new FileOutputStream(to);
				byte buf[] = new byte[1024];
				int len;
				while((len = fis.read(buf))>0) fos.write(buf,0,len);
			}catch (Exception e) {
				throw new RuntimeException(e);
			}finally{
				closeStream(fis, fos);
			}
		}
	}
	
	private static void closeStream(Closeable ... streams){
		Exception ex = null;
		if(streams != null && streams.length > 0){
			for(Closeable c : streams){
				try{
					c.close();
				}catch (Exception e) {
					if(ex != null){
						ex.setStackTrace(ArrayUtil.concat(ex.getStackTrace(), e.getStackTrace()));
					}else{
						ex = e;
					}
				}
			}
		}
		if(ex != null) throw new RuntimeException(ex);
	}
}
